package com.taxicall.client;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;


import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.taxicall.client.NetworkService.LocalBinder;
import com.taxicall.entities.NetworkMessage;
import com.taxicall.entities.NetworkMessage.MessageType;
import com.taxicall.entities.Taxi;
import com.taxicall.entities.TaxiLocation;
import com.taxicall.entities.TaxiRoute;
import com.taxicall.udptransport.SerializationUtils;
import com.taxicall.udptransport.UdpNetworkService;


public class DestinationSelection extends FragmentActivity {
	
	private final double EARTH_RADIUS = 6371000;
	private MyResultReceiver resReceiver;
	private Intent serviceIntent;

	private GoogleMap mMap;
	private Marker startingPoint;
	private Marker destination = null;
	//Taxi taxi1 = new Taxi();
	//Taxi taxi2 = new Taxi();
	private static Taxi[] taxis = new Taxi[0];
	private static boolean receivedAnswer = false, foundTaxi = false;
	private static String answer;
	private TextView waitMessage;
	private TaxiRoute clientPath;
	private NetworkService service;
	private boolean bound = false;
	private UdpNetworkService network;
	
	private ServiceConnection serviceConnection = new ServiceConnection() {
		
		@Override
		public void onServiceDisconnected(ComponentName name) {
			bound = false;
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder iBinder) {
			LocalBinder binder = (LocalBinder)iBinder;
			service = binder.getService();
			bound = true;
		}
	};
	
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_destination_selection);
		waitMessage = (TextView) findViewById(R.id.waitMessage);
		Bundle extras = getIntent().getExtras();
		final TaxiLocation tl = (TaxiLocation) extras.get("startingLocation");
		
		//taxi1.setLocation(new TaxiLocation(null, null, 43.51, 53.131));
		//taxi2.setLocation(new TaxiLocation(null, null, 37.974001, 23.733961));
		
		NetworkMessage msgToSend = new NetworkMessage(MessageType.GetAllTaxis, "");
		
		resReceiver = new MyResultReceiver(null);
		serviceIntent = new Intent(getApplicationContext(), UDPService.class);
		serviceIntent.putExtra("resultReceiver", resReceiver);
		serviceIntent.putExtra("msg", msgToSend);
		serviceIntent.putExtra("serverip", getIntent().getStringExtra("serverip"));
		startService(serviceIntent);
		
		mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
		startingPoint = mMap.addMarker(new MarkerOptions()
				.position(new LatLng(tl.getLatitude(), tl.getLongtitude()))
				.title("Starting location"));
		
		CameraPosition camPos = new CameraPosition.Builder()
				.target(new LatLng(tl.getLatitude(), tl.getLongtitude()))
				.zoom(14)
				.build();
		mMap.animateCamera(CameraUpdateFactory.newCameraPosition(camPos));
		
		mMap.setOnMapClickListener(new OnMapClickListener() {
			Polyline line;
			public void onMapClick(LatLng latlng) {
				if(line != null) {
					destination.remove();
					line.remove();
				}
				destination = mMap.addMarker(new MarkerOptions()
						.position(latlng)
						.draggable(true)
						.title("Destination"));
				
				line = mMap.addPolyline(new PolylineOptions()
							.add(startingPoint.getPosition(), destination.getPosition())
							.color(Color.RED)
							.width(5));
			}
		});
		Button submitB = (Button) findViewById(R.id.submitPath);
		
		submitB.setOnClickListener(new OnClickListener() {

			public void onClick(View arg0) {
				if(!bound)
					return;
				
				final List<Taxi> selectedTaxis = map(tl, taxis);
				clientPath = new TaxiRoute(tl, new TaxiLocation(null, null, destination.getPosition().latitude, destination.getPosition().longitude));
				//edw stelnei ta aitimata sta taksi
				
					receivedAnswer = false;
					new Thread(new Runnable() {
						
						public void run() {
							Intent intent = new Intent(getApplicationContext(), Confirmation.class);
							for(final Taxi t : selectedTaxis) {
								foundTaxi = sendToTaxi(t);
								if(foundTaxi) {
									intent.putExtra("foundTaxi", true);
									startActivity(intent);
									break;
								}
							}
							intent.putExtra("foundTaxi", false);
							startActivity(intent);
						}
					}).start();				
			}
			
		});
		bindService(new Intent(getApplicationContext(), NetworkService.class), serviceConnection, Context.BIND_AUTO_CREATE);
	}
	
	protected void onStop(){
		super.onStop();
		unbindService(serviceConnection);
	}

	private List<Taxi> map(TaxiLocation tl, Taxi[] taxis) {
		List<Taxi> retList = new ArrayList<Taxi>();
		HashMap<Taxi, Double> distances = new HashMap<Taxi, Double>();
		for(int i=0; i<taxis.length; i++) {
			TaxiLocation tl2 = taxis[i].getLocation();
			double x = Math.toRadians(tl.getLongtitude()) - Math.toRadians(tl2.getLongtitude());
			x *= Math.cos((Math.toRadians(tl.getLatitude()) + Math.toRadians(tl2.getLatitude())) / 2);
			double y = Math.toRadians(tl.getLatitude()) - Math.toRadians(tl2.getLatitude());
			y = EARTH_RADIUS * Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
			y = Math.round(y);
			distances.put(taxis[i], new Double(y));
		}
		distances = sortHashMap(distances);
		int counter = 0;
		for(Taxi tx : distances.keySet()) {
			if((distances.get(tx) <= 5000) && (counter < 3) ) {
				retList.add(tx);
				counter++;
			}
			else
				break;
			if(counter == 3)
				break;
		}
		return retList;
	}
	
	private HashMap<Taxi, Double> sortHashMap(HashMap<Taxi, Double> hm) {
		Map<Taxi, Double> tempMap = new HashMap<Taxi, Double>();
		for(Taxi tx : hm.keySet()) {
			tempMap.put(tx, hm.get(tx));
		}
		
		List<Taxi> mapKeys = new ArrayList<Taxi>(tempMap.keySet());
		List<Double> mapValues = new ArrayList<Double>(tempMap.values());
		HashMap<Taxi, Double> sortedMap = new LinkedHashMap<Taxi, Double>();
		TreeSet<Double> sortedSet = new TreeSet<Double>(mapValues);
		Object[] sortedArray = sortedSet.toArray();
		for(int i=0; i < sortedArray.length; i++) {
			sortedMap.put(mapKeys.get(mapValues.indexOf(sortedArray[i])), (Double) sortedArray[i]);
		}
		return sortedMap;
	}
	
	public static void getTaxis(List<Taxi> list) {
		taxis = new Taxi[list.size()];
		list.toArray(taxis);
	}
	
	public static void getAnswer(String ans) {
		receivedAnswer = true;
		answer = ans.toLowerCase();
	}
	
	public static void receivedTimeout() {
		receivedAnswer = true;
	}
	
	private boolean sendToTaxi(Taxi t) {
		System.out.println(">>sending to taxi with ip:"+t.getIP().toString());
		receivedAnswer = false;
		
		
		if (network == null) {
			try {
				network = service.open();
			} catch (SocketException e) {
				e.printStackTrace();
			}
		}
		if(network != null) {
			NetworkMessage msgToSend = new NetworkMessage(MessageType.RequestRide, clientPath);
			try {
				network.send(msgToSend, t.getIP(), UdpNetworkService.DEFAULT_PORT);
			}
			catch (IOException e) {
				e.printStackTrace();
				return false;
			}
		}
		
		DatagramPacket response;
		try {
			response = network.receive(120000);
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		
		if(response != null) {
			receivedAnswer = true;
			NetworkMessage taxiMsg = (NetworkMessage) SerializationUtils.deserialize(response.getData());
			answer = (String) taxiMsg.getDataObject();
			if(answer.toLowerCase() == "yes") {
				return true;
			}
		}
		return false;
		/*serviceIntent = new Intent(getApplicationContext(), UDPService.class);
		serviceIntent.putExtra("resultReceiver", resReceiver);
		serviceIntent.putExtra("msg", msgToSend);
		serviceIntent.putExtra("ip", t.getIP());
		startService(serviceIntent);*/
	}
}
